package mylab.starters.mybatis.utils;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import mylab.utils.common.DebugUtil;
import mylab.utils.common.PackageUtil;
import mylab.utils.common.ReflectUtil;
import org.mybatis.spring.SqlSessionTemplate;

import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Optional;
import java.util.Set;

/**
 * @Description:
 * @author: Sylar
 * @date: 2021年10月14日 7:39 下午
 */
@Slf4j
@UtilityClass
@SuppressWarnings({"unchecked", "rawtypes"})
public class MpUtils {

    /**
     * 查找所有顶级基类是superClass的实体类型
     *
     * @param packages   实体包名
     * @param superClass 基类
     * @param <T>        基类类型
     * @return 实体类型列表
     */
    public <T> List<Class<? extends T>> findAllEntityClass(List<String> packages, Class<T> superClass) {
        if (packages == null || packages.size() == 0) {
            packages = Lists.newArrayList();
            //查找所有实体类
            String appPackageName = PackageUtil.getAppBasePackageName();
            if (appPackageName == null && DebugUtil.isDebug()) {
                appPackageName = PackageUtil.getHeadOfPackageName(MpUtils.class);
            }
            Assert.notNull(appPackageName, "获取包名失败");
            packages.add(appPackageName);
        }

        log.debug("开始扫描子类。父类: {} , 待扫描包: {}", superClass.getSimpleName(), packages);
        Set<Class<? extends T>> sets = Sets.newHashSet();
        packages.forEach(packageName -> {
            Set<Class<? extends T>> set = ReflectUtil.getSubTypesOf(packageName, superClass);
            set.forEach(clazz -> {
                boolean isAbstract = Modifier.isAbstract(clazz.getModifiers());
                boolean isStatic = Modifier.isStatic(clazz.getModifiers());
                if (!isAbstract && !isStatic) {
                    sets.add(clazz);
                }
            });
        });

        return Lists.newArrayList(sets);
    }

    public <T> BaseMapper<T> getMapper(String tableName) {
        TableInfo tableInfo = TableInfoHelper.getTableInfo(tableName);
        return getMapper(tableInfo);
    }

    public <T> BaseMapper<T> getMapper(Class<T> entityClass) {
        TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);
        return getMapper(tableInfo);
    }

    public <T> BaseMapper<T> getMapper(TableInfo tableInfo) {
        return getMapper(SpringUtil.getBean(SqlSessionTemplate.class), tableInfo);
    }


    /**
     * 查找 Mapper 实例
     *
     * @param sqlSessionTemplate sqlSessionTemplate
     * @param tableInfo          tableInfo
     * @return Mapper实例
     */
    public <T> BaseMapper<T> getMapper(SqlSessionTemplate sqlSessionTemplate, TableInfo tableInfo) {
        String mapperName = tableInfo.getCurrentNamespace();
        Class<T> mapperClass = null;
        try {
            mapperClass = (Class<T>) Class.forName(mapperName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Assert.notNull(mapperClass, "mapperClass not found:" + mapperName);

        return (BaseMapper<T>) sqlSessionTemplate.getMapper(mapperClass);
    }

    /**
     * 根据实体的属性名称查找对应的数据库字段名称
     *
     * @param poClass      实体类型
     * @param propertyName 属性名
     * @return 字段名
     */
    public String getColumnName(Class<?> poClass, String propertyName) {
        Assert.notNull(propertyName, "propertyName is null");
        TableInfo tableInfo = TableInfoHelper.getTableInfo(poClass);

        //主键字段是KeyColumn
        if (propertyName.equals(tableInfo.getKeyProperty())) {
            //如果是id,返回KeyColumn
            return tableInfo.getKeyColumn();
        }

        //主键字段在FieldList中
        TableFieldInfo fieldInfo = getFieldInfoByProperty(poClass, propertyName);
        Assert.notNull(fieldInfo, "找不到字段信息:" + propertyName);

        String columnName = fieldInfo.getColumn();
        if (columnName == null) {
            //TODO 判断是否json字段的属性
            //josn 属性：字段->'$.json属性'， eg: "address->'$.city'" 表示 json对象address的city属性
        }
        return columnName;
    }


    /**
     * 根据属性名获取字段信息
     *
     * @param entityClass  实体类型
     * @param propertyName 属性名
     * @return 字段信息
     */
    public TableFieldInfo getFieldInfoByProperty(Class<?> entityClass, String propertyName) {
        TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);
        Optional<TableFieldInfo> optional = tableInfo.getFieldList().stream()
                .filter(field -> equalsAnyCamelOrUndreline(field.getProperty(), propertyName))
                .findFirst();

        return optional.orElse(null);
    }

    /**
     * 根据字段名获取字段信息
     *
     * @param entityClass 实体类型
     * @param columnName  字段名
     * @return 字段信息
     */
    public TableFieldInfo getFieldInfoByColumn(Class<?> entityClass, String columnName) {
        TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);
        Optional<TableFieldInfo> optional = tableInfo.getFieldList().stream()
                .filter(field -> equalsAnyCamelOrUndreline(field.getColumn(), columnName))
                .findFirst();

        return optional.orElse(null);
    }

    /**
     * 判断是不是相同属性名称， 支持 驼峰格式、下划线格式 模糊匹配
     *
     * @param property 属性名称
     * @param unknow   待比较的字符串，可能是驼峰格式，也可能是下划线格式
     * @return 是否相同
     */
    public boolean equalsAnyCamelOrUndreline(String property, String unknow) {
        if (CharSequenceUtil.isAllNotEmpty(property, unknow)) {
            return CharSequenceUtil.equals(property, unknow)
                    || CharSequenceUtil.equals(StringUtils.camelToUnderline(property), unknow);
        }
        return false;
    }


}
